using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;

namespace Framework
{
    [CreateAssetMenu]
    public class FsmData : ScriptableObject
    {
        public string Type;
        public int x;
        public int y;
        public float zoom;
        public FsmDataEntry Entry;
        public List<FsmDataStateData> States;
        public List<string> Events;
        public List<FsmDataActionData> Actions;
        public List<FsmTransitonData> Transitions;

        public FsmData()
        {
            Entry = new FsmDataEntry();
            Entry.x = Entry.y = 100;
            States = new List<FsmDataStateData>();
            Events = new List<string>();
            Actions = new List<FsmDataActionData>();
            Transitions = new List<FsmTransitonData>();
            Type = "Framework.FsmStateAction";
            x = y = 0;
            this.zoom = 1f;
        }
        public void Clear()
        {
            States.Clear();
            Events.Clear();
            Actions.Clear();
            Transitions.Clear();
        }
        public Fsm GetFsm()
        {
            var fsm = new Fsm();
            var stateList = new List<FsmState>();
            foreach (var item in States)
            {
                stateList.Add(fsm.Add(item.Name));
            }
            foreach (var item in Actions)
            {
                var state = stateList[item.State];
                System.Type type = Utils.Assembly.GetType(item.Type);
                if (type == null)
                {
                    FrameworkLog.LogErrorFormat("not find class {0}", item.Type);
                    continue;
                }
                var ins = System.Activator.CreateInstance(type);
                if (ins is FsmStateAction action)
                {
                    state.AddAction(action);
                    var fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
                    foreach (var f in fields)
                    {
                        var p = item.GetField(f.Name);
                        if (p == null) continue;
                        var t = p.GetType();
                        if (!(f.FieldType == t || f.FieldType.IsSubclassOf(t))) continue;
                        var v = p.GetValue();
                        if (v == null) continue;
                        if (f.FieldType.IsEnum)
                        {
                            var e = (Enum)Enum.ToObject(f.FieldType, p.GetInt());
                            f.SetValue(ins, e);
                        }
                        else
                        {
                            f.SetValue(ins, v);
                        }

                    }
                }
                else
                {
                    FrameworkLog.LogErrorFormat("not create class {0}", item.Type);
                    continue;
                }
            }
            foreach (var item in Transitions)
            {
                var from = stateList[item.From];
                var to = stateList[item.To];
                var e = Events[item.EventId];
                from.AddTransition(to.Name, e);
            }
            var entry = stateList[this.Entry.EntryPoint];
            fsm.Start(entry.Name);
            return fsm;
        }
    }
    [System.Serializable]
    public class FsmDataEntry
    {
        public int x;
        public int y;
        public int EntryPoint;
    }
    [System.Serializable]
    public class FsmDataStateData
    {
        public string Name;
        public int x;
        public int y;
    }
    [System.Serializable]
    public class FsmDataActionParamData
    {
        private static readonly ByteArray _byteArray = new(100);
        public string Name;
        public byte Type;
        public byte[] Params;
        public new Type GetType()
        {
            if (Type == 0)
            {
                return typeof(string);
            }
            if (Type == 1)
            {
                return typeof(int);
            }
            if (Type == 2)
            {
                return typeof(float);
            }
            if (Type == 3)
            {
                return typeof(bool);
            }
            if (Type == 4)
            {
                return typeof(Vector3);
            }
            if (Type == 5)
            {
                return typeof(Enum);
            }
            if (Type == 6)
            {
                return typeof(Vector2);
            }
            return null;
        }
        public void Set(string value)
        {
            Type = 0;
            _byteArray.Write(value);
            var len = _byteArray.Position;
            Params = new byte[len];
            _byteArray.CopyTo(Params, len);
            _byteArray.Clear();
        }
        public void Set(int value)
        {
            Type = 1;
            _byteArray.Write(value);
            var len = _byteArray.Position;
            Params = new byte[len];
            _byteArray.CopyTo(Params, len);
            _byteArray.Clear();
        }
        public void Set(float value)
        {
            Type = 2;
            _byteArray.Write(value);
            var len = _byteArray.Position;
            Params = new byte[len];
            _byteArray.CopyTo(Params, len);
            _byteArray.Clear();
        }
        public void Set(bool value)
        {
            Type = 3;
            _byteArray.Write(value);
            var len = _byteArray.Position;
            Params = new byte[len];
            _byteArray.CopyTo(Params, len);
            _byteArray.Clear();
        }
        public void Set(Vector3 value)
        {
            Type = 4;
            _byteArray.Write(value.x);
            _byteArray.Write(value.y);
            _byteArray.Write(value.z);
            var len = _byteArray.Position;
            Params = new byte[len];
            _byteArray.CopyTo(Params, len);
            _byteArray.Clear();
        }
        public void Set(Enum value)
        {
            Type = 5;
            _byteArray.Write(Convert.ToInt32(value));
            var len = _byteArray.Position;
            Params = new byte[len];
            _byteArray.CopyTo(Params, len);
            _byteArray.Clear();
        }
        public void Set(Vector2 value)
        {
            Type = 6;
            _byteArray.Write(value.x);
            _byteArray.Write(value.y);
            var len = _byteArray.Position;
            Params = new byte[len];
            _byteArray.CopyTo(Params, len);
            _byteArray.Clear();
        }

        public object GetValue()
        {
            if (Type == 0)
            {
                return GetString();
            }
            if (Type == 1)
            {
                return GetInt();
            }
            if (Type == 2)
            {
                return GetFloat();
            }
            if (Type == 3)
            {
                return GetBool();
            }
            if (Type == 4)
            {
                return GetVector3();
            }
            if (Type == 5)
            {
                return GetInt();
            }
            if (Type == 6)
            {
                return GetVector2();
            }
            return null;
        }
        public string GetString()
        {
            _byteArray.Clear();
            _byteArray.Buffer.Write<byte>(0,Params,0);
            return _byteArray.ReadString();
        }
        public int GetInt()
        {
            _byteArray.Clear();
            _byteArray.Buffer.Write<byte>(0,Params,0);
            return _byteArray.ReadInt();
        }
        public float GetFloat()
        {
            _byteArray.Clear();
            _byteArray.Buffer.Write<byte>(0,Params,0);
            return _byteArray.ReadFloat();
        }
        public bool GetBool()
        {
            _byteArray.Clear();
            _byteArray.Buffer.Write<byte>(0,Params,0);
            return _byteArray.ReadBool();
        }
        public Vector3 GetVector3()
        {
            _byteArray.Clear();
            _byteArray.Buffer.Write<byte>(0,Params,0);
            Vector3 v3 = new Vector3();
            v3.x = _byteArray.ReadFloat();
            v3.y = _byteArray.ReadFloat();
            v3.z = _byteArray.ReadFloat();
            return v3;
        }
        public Vector2 GetVector2()
        {
            _byteArray.Clear();
            _byteArray.Buffer.Write<byte>(0,Params,0);
            Vector2 v2 = new Vector2();
            v2.x = _byteArray.ReadFloat();
            v2.y = _byteArray.ReadFloat();
            return v2;
        }
    }
    [System.Serializable]
    public class FsmDataActionData
    {
        public string Type;
        public string Name;
        public int State;
        public FsmDataActionParamData[] Params;
        public FsmDataActionParamData GetField(string name)
        {
            foreach (var item in Params)
            {
                if (item.Name == name)
                {
                    return item;
                }
            }
            return null;
        }
    }
    [System.Serializable]
    public class FsmTransitonData
    {
        public int EventId;
        public int From;
        public int To;
    }
}